package com.linx.simpleadapter

import android.content.Context
import android.util.SparseArray
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.linx.simpleadapter.ViewHolder.Companion.getViewHolder
import kotlinx.android.extensions.LayoutContainer

open class SimpleAdapter<T> protected constructor(layout: Int = -1,
                                                  itemCallback: DiffUtil.ItemCallback<T> = defaultItemCallback(),
                                                  private inline val onBind: ViewHolder<T>.(item: T) -> Unit,
                                                  private inline var onCreate: ViewHolder<T>.(viewType: Int) -> Unit = {},
                                                  private inline val newItemView: (parent: ViewGroup, viewType: Int) -> View = { _, _ ->
                                                      throw RuntimeException("newItemView must be implemented when layout is not set!")
                                                  })
    : ListAdapter<T, ViewHolder<T>>(itemCallback) {

    private var mLayoutId: Int = layout

    constructor(layout: Int, itemCallback: DiffUtil.ItemCallback<T> = defaultItemCallback(),
                onBind: ViewHolder<T>.(item: T) -> Unit) : this(layout, itemCallback, onBind = onBind,
            onCreate = {})

    constructor(newItemView: (parent: ViewGroup, viewType: Int) -> View,
                itemCallback: DiffUtil.ItemCallback<T> = defaultItemCallback(),
                onBind: ViewHolder<T>.(item: T) -> Unit) : this(layout = -1,
            itemCallback = itemCallback, onBind = onBind, newItemView = newItemView)

    fun bindEvent(onCreate: ViewHolder<T>.(viewType: Int) -> Unit): SimpleAdapter<T> {
        this.onCreate = onCreate
        return this
    }

    fun item(pos: Int): T {
        return getItem(pos)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<T> {
        val holder = if (mLayoutId != -1) {
            getViewHolder(parent.context, this, null, parent, mLayoutId)
        } else {
            getViewHolder(newItemView(parent, viewType), this)
        }
        holder.onCreate(viewType)
        return holder
    }

    override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) {
        holder.onBind(getItem(position))
    }
}

private fun <T> defaultItemCallback(): DiffUtil.ItemCallback<T> {
    return object : DiffUtil.ItemCallback<T>() {
        override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
            return oldItem == newItem
        }

        override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
            return oldItem == newItem
        }
    }
}

@Suppress("UNCHECKED_CAST")
class ViewHolder<T>(override val containerView: View, val adapter: SimpleAdapter<T>) : RecyclerView.ViewHolder(containerView), LayoutContainer {
    companion object {
        fun <T> getViewHolder(context: Context, adapter: SimpleAdapter<T>, convertView: View?, parent: ViewGroup, layoutId: Int): ViewHolder<T> {
            if (convertView == null) {
                val view = LayoutInflater.from(context).inflate(layoutId, parent, false)
                val holder = ViewHolder(view, adapter)
                view.tag = holder
                return holder
            }
            return convertView.tag as ViewHolder<T>
        }

        fun <T> getViewHolder(view: View, adapter: SimpleAdapter<T>): ViewHolder<T> = ViewHolder(view, adapter)
    }

    private val views = SparseArray<View>()
    private val convertView: View = containerView

    @Suppress("UNCHECKED_CAST")
    fun <V : View> view(res: Int): V {
        if (views.get(res) == null) {
            views.put(res, convertView.findViewById(res))
        }
        return views.get(res) as V
    }

}